import copy
import sys
import os
import shutil
from PyQt5.QtWidgets import *
from pyqtgraph import QtCore, QtGui
import os
import configparser
import globalvar
from datetime import timedelta
from datetime import datetime
import multiprocessing
from multiprocessing import Pipe
import time
import importlib
from vnctpmdType661 import *
from PyQt5.QtCore import Qt, pyqtSignal
from pathlib import Path
from multiprocessing import Process, Queue
import numba as nb
from contextlib import closing
import numpy as np
from ctypes import c_char_p

global dict_instrumentgroup, dict_instrumentgrouprun
dict_instrumentgroup = {}
dict_instrumentgrouprun = {}
import globalType
import random
import module_backtestwindow
import module_strategy
import threading
import inspect
import ctypes
import globalType
# 共享内存
import mmap
import contextlib
# 获取系统信息的模块
import psutil

# 定义合约参数，出现新合约品种可以自己添加，合约代码:[每手吨数*保证金,每手吨数]
dict_cx = {'rb': [10 * 0.1, 10],
           'RM': [10 * 0.1, 10],
           'ru': [10 * 0.14, 10],
           'SM': [5 * 0.10, 5],
           'CF': [5 * 0.10, 5],
           'zn': [5 * 0.11, 5],
           'al': [5 * 0.09, 5],
           'cu': [5 * 0.14, 5],
           'au': [1000 * 0.12, 1000],
           'ag': [15 * 0.14, 15],
           'hc': [10 * 0.1, 10],
           'bu': [10 * 0.13, 10],
           'ni': [1 * 0.14, 1],
           'i': [100 * 0.11, 100],
           'TA': [5 * 0.12, 5],
           'a': [10 * 0.15, 10],
           'm': [10 * 0.1, 10],
           'y': [10 * 0.1, 10],
           'p': [5 * 0.12, 5],
           'MA': [10 * 0.10, 10],
           'pp': [5 * 0.11, 5],
           'cs': [10 * 0.08, 10],
           'fg': [20 * 0.1, 20],
           'l': [5 * 0.12, 5],
           'v': [5 * 0.12, 5],
           'SR': [10 * 0.10, 10],
           'c': [10 * 0.10, 10],
           'SF': [5 * 0.10, 5],
           'IC': [200 * 0.12, 200],
           'IF': [300 * 0.12, 300],
           'IH': [300 * 0.12, 300],
           'T': [10000 * 0.03, 10000],
           'TF': [10000 * 0.03, 10000],
           'TS': [10000 * 0.03, 10000],
           'j': [100 * 0.15, 100],
           'ZC': [100 * 0.10, 100],
           'jd': [5 * 0.12, 5],
           'AP': [10 * 0.12, 10],
           'sp': [10 * 0.12, 10],
           'CY': [5 * 0.12, 5],
           'BC': [5 * 0.12, 5],
           'CJ': [5 * 0.12, 5],
           'GN': [5 * 0.12, 5],
           'LH': [5 * 0.12, 5],
           'LU': [5 * 0.12, 5],
           'JR': [20 * 0.12, 20],
           'PF': [5 * 0.12, 5],
           'PG': [5 * 0.12, 5],
           'PK': [5 * 0.12, 5],
           'SA': [20 * 0.12, 20],
           'UR': [5 * 0.12, 5],
           'eg': [10 * 0.12, 10]}


# 获取合约参数
def GetCX(InstrumentID):
    return dict_cx[InstrumentID.strip('0123456789')]

# 用于回测的虚拟账号父类
class VirtualAccount(object):
    def __init__(self, period, slippoint):
        self.period = period
        self.dict_strategyinstrument = {}
        self.dick_tick = {}
        self.list_tick = []
        self.fee = 0.0
        self.buyposition = 0
        self.sellposition = 0
        self.slippoint = slippoint
        self.jump = 1
        self.exchange = 'SHFE'
        self.feetype = VN_FEE_RATE
        self.buyvol = 0
        self.sellvol = 0
        self.buyvol_history = 0
        self.sellvol_history = 0
        self.buyprice = 0.0
        self.sellprice = 0.0
        self.LastPrice = 0.0
        self.initamount = 500000.0
        self.amount = self.initamount
        self.useramount = self.initamount
        self.BidPrice = 0.0
        self.AskPrice = 0.0
        self.todayCloseProfit = 0.0
        self.todayPositionProfit = 0.0
        self.todayPreBalance = 0.0
        self.totalfee = 0.0
        self.buyopennum = 0
        self.sellopennum = 0
        self.buyclosenum = 0
        self.sellclosenum = 0
        self.profitlossratio = 1
        self.sharperatio = 0
        self.sortino = 0
        self.winningrate = 0
        self.winningrate_winnum = 0
        self.winningrate_losenum = 0
        self.winningrate_drawnum = 0

        self.feetype = VN_FEE_RATE
        self.OpenFee = 0.0
        self.CloseFee = 0.0

    # @nb.jit(nogil=True, nopython=True)
    def evaluation(self):
        return [1, 1, 1, 1, 1]

    # 用于回测
    def OnKline(self, reportpath1, reportpath2, arg, mddata, filter):
        self.reportpath1 = reportpath1
        self.reportpath2 = reportpath2
        self.arg = arg
        self.TradingDay = mddata.TradingDay
        self.klinetime = mddata.klinetime
        self.filter = filter

    # 保存回测结果到文件
    def AddTradingRecord(self, InstrumentID, ExchangeID, direction, OffsetFlag, OrderPriceType, price, thisvol):
        pathname = '%s/tradingrecord/' % (self.reportpath2)
        filename = '%s/tradingrecord/%d_%d_%d_%d_%d_%d.txt' % (
            # pathname = '%s/' % (self.reportpath2)
            # filename = '%s/%d_%d_%d_%d_%d_%d.txt' % (
            self.reportpath2, self.arg[0], self.arg[1], self.arg[2], self.arg[3], self.arg[4], self.arg[5])
        if not Path(self.reportpath1).is_dir():
            os.makedirs(self.reportpath1)
        if not Path(self.reportpath2).is_dir():
            os.makedirs(self.reportpath2)
        if not Path(pathname).is_dir():
            os.makedirs(pathname)
        if direction == '0':
            str1 = '买'
        else:
            str1 = '卖'
        if OffsetFlag == '0':
            str2 = '开仓'
        elif OffsetFlag == '3':
            str2 = '平今'
        elif OffsetFlag == '1':
            str2 = '平仓'
        with open(filename, "a") as file:
            line = '%s,%s,%s,%s,%s,%.2f,%d\n' % (
                self.TradingDay, self.klinetime, InstrumentID, str1, str2, price, thisvol)
            file.write(line)
        file.close()

    # 量化回测用的下单方法
    def InsertOrder_backtest2(self, InstrumentID, exchangeid, direction, offsetflag, orderpricetype, price, vol):
        self.AddTradingRecord(InstrumentID, exchangeid, direction, offsetflag, orderpricetype, price, vol)
        # 获得该合约参数
        self.askprice = price
        self.bidprice = price

        try:
            c = GetCX(self.InstrumentID)
        except Exception as e:
            print('GetCX error:', e)
            return
        cx = c[0]
        cy = c[1]
        self.fee = 0
        if self.feetype == VN_FEE_RATE:
            # 按比例
            if direction == THOST_FTDC_D_Buy:
                if offsetflag == THOST_FTDC_OF_Open:
                    self.fee = self.OpenFee * self.askprice * cy
                else:
                    self.fee = self.CloseFee * self.askprice * cy
            else:
                if offsetflag == THOST_FTDC_OF_Open:
                    self.fee = self.OpenFee * self.bidprice * cy
                else:
                    self.fee = self.CloseFee * self.bidprice * cy
        elif self.feetype == VN_FEE_CONSTANT:
            # 固定金额
            if offsetflag == THOST_FTDC_OF_Open:
                self.fee = self.OpenFee
            else:
                self.fee = self.CloseFee

        if offsetflag == THOST_FTDC_OF_Open:
            allownum = float(vol)
            # zmin(vol, CalUpdateBigAmount(0, addslip_bidprice, self.useramount, & cx, & cy))
            if direction == THOST_FTDC_D_Buy:
                self.buyopennum += 1
                # print("买开%d\n", self.buyvol)
                addslip_askprice = self.askprice + self.slippoint * self.jump
                cz = float(self.buyvol + vol)
                # 保证有足够的资金开新仓
                if self.useramount - allownum * (addslip_askprice * cx + self.fee) > 1e-7:
                    if cz > 1e-7:
                        self.buyprice = (self.buyprice * float(
                            self.buyvol + self.buyvol_history) + addslip_askprice * allownum) / cz
                        self.buyvol = self.buyvol + vol
                        # amount = amount - allownum * fee
                        self.useramount = self.useramount - allownum * (addslip_askprice * cx + self.fee)
                        self.totalfee = self.totalfee + allownum * self.fee
            else:
                self.sellopennum += 1
                # print("卖开%d\n", self.sellvol)
                addslip_bidprice = self.bidprice - self.slippoint * self.jump
                cz = float(self.sellvol + vol)
                # 保证有足够的资金开新仓
                if self.useramount - allownum * (addslip_bidprice * cx + self.fee) > 1e-7:
                    if cz > 1e-7:
                        self.sellprice = (self.sellprice * float(
                            self.sellvol + self.sellvol_history) + addslip_bidprice * allownum) / cz
                        self.sellvol = self.sellvol + vol
                        # amount = amount - allownum * fee
                        self.useramount = self.useramount - allownum * (addslip_bidprice * cx + self.fee)
                        self.totalfee = self.totalfee + allownum * self.fee
        elif offsetflag == THOST_FTDC_OF_Close or offsetflag == THOST_FTDC_OF_CloseToday:
            # print("z_close\n")
            if direction == THOST_FTDC_D_Buy:
                # print("z_多\n")
                self.buyclosenum += 1
                # 买平，平今
                if offsetflag == THOST_FTDC_OF_CloseToday:
                    # print("z_平今[%d>%d]\n", self.sellvol, vol)
                    if self.sellvol >= vol:
                        # print("z_条件\n")
                        allownum = float(vol)
                        # 资金曲线，持仓并不是动态的，平仓才earn
                        addslip_askprice = self.askprice + float(self.slippoint) * float(self.jump)
                        thisCloseProfit = allownum * (cy * (self.sellprice - addslip_askprice) - self.fee)
                        self.sellvol = self.sellvol - vol
                        self.useramount = self.useramount + allownum * (self.sellprice * cx) + thisCloseProfit
                        self.totalfee = self.totalfee + allownum * self.fee

                        if self.sellprice - self.askprice > 1e-7:
                            self.winningrate_winnum += 1
                        elif self.sellprice - self.askprice < -1e-7:
                            self.winningrate_losenum += 1
                        else:
                            self.winningrate_drawnum += 1
                        totalnum = float(self.winningrate_winnum + self.winningrate_losenum + self.winningrate_drawnum)
                        if totalnum > 1e-7:
                            self.winningrate = float(self.winningrate_winnum) / totalnum
                        else:
                            self.winningrate = 0.0

                        # 新增平仓权益计算
                        self.todayCloseProfit = self.todayCloseProfit + thisCloseProfit
                        # 新增持仓权益计算F
                        self.todayPositionProfit = (self.sellprice - price) * float(
                            self.sellvol + self.sellvol_history) + \
                                                   (price - self.buyprice) * float(self.buyvol + self.buyvol_history)
                        if self.sellvol == 0 and self.sellvol_history == 0:
                            self.sellprice = 0
                    else:
                        pass
                        # 下单失败回调
                else:
                    # print("z_平昨[%d>%d]\n", self.sellvol_history, vol)
                    if exchangeid == "SHFE":
                        # 买平，平仓
                        if self.sellvol_history >= vol:
                            allownum = float(vol)
                            # 资金曲线，持仓并不是动态的，平仓才earn
                            addslip_askprice = self.askprice + float(self.slippoint) * self.jump
                            # 本次盈利
                            self.thisCloseProfit = allownum * (cy * (self.sellprice - addslip_askprice) - self.fee)
                            self.sellvol_history = self.sellvol_history - vol
                            self.useramount = self.useramount + allownum * (self.sellprice * cx) + self.thisCloseProfit
                            self.totalfee = self.totalfee + allownum * self.fee

                            if self.sellprice - self.askprice > 1e-7:
                                self.winningrate_winnum += 1
                            elif self.sellprice - self.askprice < -1e-7:
                                self.winningrate_losenum = 7
                            else:
                                self.winningrate_drawnum = 9
                            totalnum = float(
                                self.winningrate_winnum + self.winningrate_losenum + self.winningrate_drawnum)
                            if totalnum > 1e-7:
                                self.winningrate = float(self.winningrate_winnum) / totalnum
                            else:
                                self.winningrate = 0

                            # 新增平仓权益计算
                            self.todayCloseProfit = self.todayCloseProfit + self.thisCloseProfit
                            # 新增持仓权益计算F
                            self.todayPositionProfit = (self.sellprice - price) * float(
                                self.sellvol + self.sellvol_history) + \
                                                       (price - self.buyprice) * float(
                                self.buyvol + self.buyvol_history)
                            if self.sellvol == 0 and self.sellvol_history == 0:
                                self.sellprice = 0
                        else:
                            pass
                            # 下单失败回调
                    else:
                        # 买平，平仓
                        if self.sellvol_history >= vol:
                            # print("z_条件\n")
                            allownum = float(vol)
                            # 资金曲线，持仓并不是动态的，平仓才earn
                            addslip_askprice = self.askprice + float(self.slippoint) * self.jump
                            self.thisCloseProfit = allownum * (cy * (self.sellprice - addslip_askprice) - self.fee)
                            self.sellvol_history = self.sellvol_history - vol
                            self.useramount = self.useramount + allownum * (
                                    self.sellprice * cx) + self.thisCloseProfit
                            self.totalfee = self.totalfee + allownum * self.fee
                            if self.sellprice - self.askprice > 1e-7:
                                self.winningrate_winnum += 1
                            elif self.sellprice - self.askprice < -1e-7:
                                self.winningrate_losenum = 11
                            else:
                                self.winningrate_drawnum = 13
                            totalnum = float(
                                self.winningrate_winnum + self.winningrate_losenum + self.winningrate_drawnum)
                            if totalnum > 1e-7:
                                self.winningrate = float(self.winningrate_winnum) / totalnum
                            else:
                                self.winningrate = 0
                            # 新增平仓权益计算
                            self.todayCloseProfit = self.todayCloseProfit + self.thisCloseProfit
                            # 新增持仓权益计算
                            self.todayPositionProfit = (self.sellprice - price) * float(
                                self.sellvol + self.sellvol_history) + (price - self.buyprice) * float(
                                self.buyvol + self.buyvol_history)
                            if self.sellvol == 0 and self.sellvol_history == 0: self.sellprice = 0
                        elif self.sellvol >= vol:
                            # print("z_条件\n")
                            allownum = float(vol)
                            # 资金曲线，持仓并不是动态的，平仓才earn
                            addslip_askprice = self.askprice + float(self.slippoint) * self.jump
                            self.thisCloseProfit = allownum * (
                                    cy * (self.sellprice - addslip_askprice) - self.fee)
                            self.sellvol = self.sellvol - vol
                            self.useramount = self.useramount + allownum * (
                                    self.sellprice * cx) + self.thisCloseProfit
                            self.totalfee = self.totalfee + allownum * self.fee

                            if self.sellprice - self.askprice > 1e-7:
                                self.winningrate_winnum += 1
                            elif self.sellprice - self.askprice < -1e-7:
                                self.winningrate_losenum += 1
                            else:
                                self.winningrate_drawnum += 1
                            totalnum = float(
                                self.winningrate_winnum + self.winningrate_losenum + self.winningrate_drawnum)
                            if totalnum > 1e-7:
                                self.winningrate = float(self.winningrate_winnum) / totalnum
                            else:
                                self.winningrate = 0

                            # 新增平仓权益计算
                            self.todayCloseProfit = self.todayCloseProfit + self.thisCloseProfit
                            # 新增持仓权益计算F
                            self.todayPositionProfit = (self.sellprice - price) * float(
                                self.sellvol + self.sellvol_history) + \
                                                       (price - self.buyprice) * float(
                                self.buyvol + self.buyvol_history)

                            if self.sellvol == 0 and self.sellvol_history == 0:
                                self.sellprice = 0
                        else:
                            pass
                            # 下单失败回调
            else:
                self.sellclosenum += 1
                if offsetflag == THOST_FTDC_OF_CloseToday:
                    # print("z_平今[%d>%d]\n", self.buyvol, vol)
                    # 卖平，平今
                    if self.buyvol >= vol:
                        # print("z_条件")
                        allownum = float(vol)
                        addslip_bidprice = self.bidprice - float(self.slippoint) * self.jump
                        self.thisCloseProfit = allownum * (cy * (addslip_bidprice - self.buyprice) - self.fee)
                        self.buyvol = self.buyvol - vol
                        self.useramount = self.useramount + allownum * (self.buyprice * cx) + self.thisCloseProfit
                        self.totalfee = self.totalfee + allownum * self.fee

                        if self.bidprice - self.buyprice > 1e-7:
                            self.winningrate_winnum += 1
                        elif self.bidprice - self.buyprice < -1e-7:
                            self.winningrate_losenum += 1
                        else:
                            self.winningrate_drawnum += 1
                        totalnum = float(self.winningrate_winnum + self.winningrate_losenum + self.winningrate_drawnum)
                        if totalnum > 1e-7:
                            self.winningrate = float(self.winningrate_winnum) / totalnum
                        else:
                            self.winningrate = 0

                        # 新增收盘权益计算
                        self.todayCloseProfit = self.todayCloseProfit + self.thisCloseProfit
                        # 新增持仓权益计算
                        self.todayPositionProfit = (self.sellprice - price) * float(
                            self.sellvol + self.sellvol_history) + (price - self.buyprice) * float(
                            self.buyvol + self.buyvol_history)
                        if self.buyvol == 0 and self.buyvol_history == 0:
                            self.buyprice = 0
                    else:
                        # 下单失败回调
                        pass
                else:
                    # print("z_平昨[%d>%d]\n", self.buyvol_history, vol)
                    if exchangeid == "SHFE":
                        # 卖平，平仓
                        if self.buyvol_history >= vol:
                            allownum = float(vol)
                            addslip_bidprice = self.bidprice - float(self.slippoint) * self.jump
                            thisCloseProfit = allownum * (cy * (addslip_bidprice - self.buyprice) - self.fee)
                            self.buyvol_history = self.buyvol_history - vol
                            self.useramount = self.useramount + allownum * (self.buyprice * cx) + thisCloseProfit
                            self.totalfee = self.totalfee + vol * self.fee

                            if self.bidprice - self.buyprice > 1e-7:
                                self.winningrate_winnum += 1
                            elif self.bidprice - self.buyprice < -1e-7:
                                self.winningrate_losenum += 1
                            else:
                                self.winningrate_drawnum += 1
                            totalnum = float(
                                self.winningrate_winnum + self.winningrate_losenum + self.winningrate_drawnum)
                            if totalnum > 1e-7:
                                self.winningrate = float(self.winningrate_winnum) / totalnum
                            else:
                                self.winningrate = 0

                            # 新增收盘权益计算
                            self.todayCloseProfit = self.todayCloseProfit + thisCloseProfit
                            # 新增持仓权益计算F
                            self.todayPositionProfit = (self.sellprice - price) * float(
                                self.sellvol + self.sellvol_history) + (price - self.buyprice) * float(
                                self.buyvol + self.buyvol_history)
                            if self.buyvol == 0 and self.buyvol_history == 0:
                                self.buyprice = 0
                        else:
                            # 下单失败回调
                            pass
                    else:
                        # 卖平，平仓
                        if self.buyvol_history >= vol:
                            # print("z_条件\n")
                            allownum = float(vol)
                            addslip_bidprice = self.bidprice - float(self.slippoint) * self.jump
                            self.thisCloseProfit = allownum * (cy * (addslip_bidprice - self.buyprice) - self.fee)
                            self.buyvol_history = self.buyvol_history - vol
                            self.useramount = self.useramount + allownum * (self.buyprice * cx) + self.thisCloseProfit
                            self.totalfee = self.totalfee + vol * self.fee

                            if self.bidprice - self.buyprice > 1e-7:
                                self.winningrate_winnum += 1
                            elif self.bidprice - self.buyprice < -1e-7:
                                self.winningrate_losenum += 1
                            else:
                                self.winningrate_drawnum += 1

                            totalnum = float(
                                self.winningrate_winnum + self.winningrate_losenum + self.winningrate_drawnum)
                            if totalnum > 1e-7:
                                self.winningrate = float(self.winningrate_winnum) / totalnum
                            else:
                                self.winningrate = 0

                            # 新增收盘权益计算
                            self.todayCloseProfit = self.todayCloseProfit + self.thisCloseProfit
                            # 新增持仓权益计算
                            self.todayPositionProfit = (self.sellprice - price) * float(
                                self.sellvol + self.sellvol_history) + (price - self.buyprice) * float(
                                self.buyvol + self.buyvol_history)
                            if self.buyvol == 0 and self.buyvol_history == 0:
                                self.buyprice = 0
                        elif self.buyvol >= vol:
                            # print("z_条件\n")
                            allownum = float(vol)
                            addslip_bidprice = self.bidprice - float(self.slippoint * self.jump)
                            self.thisCloseProfit = allownum * (cy * (addslip_bidprice - self.buyprice) - self.fee)
                            self.buyvol = self.buyvol - vol
                            self.useramount = self.useramount + allownum * (self.buyprice * cx) + self.thisCloseProfit
                            self.totalfee = self.totalfee + vol * self.fee

                            if self.bidprice - self.buyprice > 1e-7:
                                self.winningrate_winnum += 1
                            elif self.bidprice - self.buyprice < -1e-7:
                                self.winningrate_losenum += 1
                            else:
                                self.winningrate_drawnum += 1
                            totalnum = float(
                                self.winningrate_winnum + self.winningrate_losenum + self.winningrate_drawnum)

                            if totalnum > 1e-7:
                                self.winningrate = float(self.winningrate_winnum) / totalnum
                            else:
                                self.winningrate = 0

                            # 新增收盘权益计算
                            self.todayCloseProfit = self.todayCloseProfit + self.thisCloseProfit
                            # 新增持仓权益计算F
                            self.todayPositionProfit = (self.sellprice - price) * float(
                                self.sellvol + self.sellvol_history) + (price - self.buyprice) * float(
                                self.buyvol + self.buyvol_history)
                            if self.buyvol == 0 and self.buyvol_history == 0:
                                self.buyprice = 0
                        else:
                            pass
                            # 下单失败回调

    # @nb.jit(nogil=True, nopython=True)
    def InsertOrder_backtest(self, InstrumentID, exchangeid, direction, offsetflag, orderpricetype, price, vol):
        if offsetflag == THOST_FTDC_OF_Open:
            self.InsertOrder_backtest2(InstrumentID, exchangeid, direction, offsetflag, orderpricetype, price, vol)
        elif offsetflag == THOST_FTDC_OF_Close:
            self.InsertOrder_backtest2(InstrumentID, exchangeid, direction, offsetflag, orderpricetype, price, vol)
        return 0

    # 实盘下单方法
    # @nb.jit(nogil=True, nopython=True)
    def InsertOrder2(self, InstrumentID, exchangeid, direction, offsetflag, orderpricetype, price, vol):
        if offsetflag == THOST_FTDC_OF_Open:
            globalvar.td.InsertOrder(InstrumentID, exchangeid, direction, offsetflag, orderpricetype, price, vol)
        elif offsetflag == THOST_FTDC_OF_Close:
            globalvar.td.InsertOrder(InstrumentID, exchangeid, direction, offsetflag, orderpricetype, price, vol)
        return 0

    # 检查是否存储该合约参数的信息，如果没有请自行在 dict_strategyinstrument 添加
    def checkinstrumentID(self, marketdata, strategyname):
        if strategyname in globalvar.dict_strategyinstrument:
            if str(marketdata.InstrumentID, encoding="utf-8") in globalvar.dict_strategyinstrument[strategyname]:
                return 0
            else:
                return 1
        else:
            return 1


# 从文件读取数据文件信息
def Function_ReadDataList(tablename, showcheck):
    for root, dirs, files in os.walk(r"./backtestdata"):
        for name in files:
            if name[len(name) - 4:] != '.csv':
                continue
            print(name)
            # print(os.path.join(root, name))
            row_cnt = tablename.rowCount()  # 返回当前行数（尾部）
            tablename.insertRow(row_cnt)  # 尾部插入一行新行表格
            # for column in range(column_cnt):
            # item = QTableWidgetItem(str(row_cnt + 1))
            # tablename.setItem(row_cnt, 0, item)
            item = QTableWidgetItem(name)
            if showcheck:
                item.setCheckState(QtCore.Qt.Checked)
            tablename.setItem(row_cnt, 1, item)


# 量化回测窗口
class BackTest(object):
    def __init__(self, signal_backtest_processbar, signal_backtest_result, signal_backtest_loaddata,
                 signal_backtest_addresult, signal_backtest_adjustmentprice):
        self.signal_backtest_processbar = signal_backtest_processbar
        self.signal_backtest_result = signal_backtest_result
        self.signal_backtest_loaddata = signal_backtest_loaddata
        self.signal_backtest_addresult = signal_backtest_addresult
        self.signal_backtest_adjustmentprice = signal_backtest_adjustmentprice


# 多进程回测窗口CPU调度规则
class StrategyCalculate(object):
    dict_cpu = {100: [16, 2], 99: [16, 8], 98: [16, 24], 97: [16, 72], 96: [16, 192], 95: [16, 512], 94: [16, 1536],
                93: [16, 3072],
                92: [0, 1], 91: [0, 1], 90: [0, 1], 89: [0, 1], 88: [0, 1],
                87: [0, 1], 86: [0, 1], 85: [0, 1], 84: [0, 1], 83: [0, 1], 82: [0, 1], 81: [0, 1], 80: [0, 1],
                79: [0, 1], 78: [0, 1], 77: [0, 1], 76: [0, 1], 75: [0, 1],
                74: [0, 1], 73: [0, 1], 72: [0, 1], 71: [0, 1], 70: [0, 1], 69: [0, 1], 68: [0, 1], 67: [0, 1],
                66: [0, 1], 65: [0, 1],
                64: [0, 1], 63: [0, 1], 62: [0, 1], 61: [0, 1], 60: [0, 1], 59: [0, 1], 58: [0, 1], 57: [0, 1],
                56: [0, 1], 55: [0, 1],
                54: [0, 1], 53: [0, 1], 52: [0, 1], 51: [0, 1], 50: [0, 1], 49: [0, 1], 48: [0, 1], 47: [0, 1],
                46: [0, 1], 45: [0, 1],
                44: [0, 1], 43: [0, 1], 42: [0, 1], 41: [0, 1], 40: [0, 1], 39: [0, 1], 38: [0, 1], 37: [0, 1],
                36: [0, 1], 35: [0, 1],
                34: [0, 1], 33: [0, 1], 32: [0, 1], 31: [0, 1], 30: [0, 1], 29: [0, 1], 28: [0, 1], 27: [0, 1],
                26: [0, 1], 25: [0, 1],
                24: [0, 1], 23: [0, 1], 22: [0, 1], 21: [0, 1], 20: [0, 1], 19: [0, 1], 18: [0, 1], 17: [0, 1],
                16: [0, 1], 15: [0, 1],
                14: [0, 1], 13: [0, 1], 12: [0, 1], 11: [0, 1], 10: [0, 1], 9: [0, 1], 8: [0, 1], 7: [0, 1], 6: [0, 1],
                5: [0, 1],
                4: [0, 1], 3: [0, 1], 2: [0, 1], 1: [0, 1], 0: [0, 1]}

    def __init__(self, point):
        self.point = point
        self.list_backtestfile = []

    def __del__(self):
        if len(self.list_backtestfile) > 0:
            self.list_backtestfile.clean()
        # print("StrategyCalculate.__del__()")

    # 将资金曲线数据存在list，待达到一定数量再集中写入，降低IO占用
    def GenerateBackTestEquityCurve(self, TradingDay, TradeingTime, useramount):
        line = '%s,%s,%s\n' % (TradingDay, TradeingTime, str(useramount))
        self.list_backtestfile.append(line)

    # 写入量化回测资金曲线数据
    def WirteBackTestEquityCurve(self, path, path2, arg):
        if len(self.list_backtestfile) > 0:
            if not Path(path).is_dir():
                os.makedirs(path)
            if not Path(path2).is_dir():
                os.makedirs(path2)
            with open('%s/%d_%d_%d_%d_%d_%d.txt' % (path2, arg[0], arg[1], arg[2], arg[3], arg[4], arg[5]),
                      "a") as file:
                for i in range(len(self.list_backtestfile)):
                    time.sleep(0)
                    QApplication.processEvents()
                    file.write(self.list_backtestfile[i])
            file.close()
            self.list_backtestfile.clear()

    # 删除字符串多余的空格
    def DeleteSpace(self, str):
        lastlengh = 0
        while (lastlengh != len(str)):
            str = str.replace(" ", "")
            lastlengh = len(str)
        return str

    # 动态加载策略或过滤规则
    def dynamic_import(self, module):
        return importlib.import_module(module)

    # 读取文件后从内存读取
    def StrategyCalculate_memory(self, strategyname, mainstrategyname, reportpath1, reportpath2, arg,
                                 refreshfrequency,
                                 splipoint, period, csvfile, linenum, instrumentid, q, memory_datalist, diffms):

        module = self.dynamic_import('strategyfilebacktest.' + mainstrategyname)
        ms = module.MyStrategy(period, splipoint)
        firstline = True
        readlinenum = 0
        byteadd = 0
        timepath = reportpath1
        self.list_backtestfile = []
        fileinfo = os.stat(csvfile)
        self.cpu_res = 0
        for line in memory_datalist:
            time.sleep(0)
            QApplication.processEvents()
            byteadd = byteadd + len(line) + 1

            if readlinenum % 10 == 0:
                self.cpu_res = float(psutil.cpu_percent())
                re = self.dict_cpu[int(self.cpu_res)]
                self.msecond = re[0]
                self.rand = re[1]
                if readlinenum % 200 == 0:
                    # q.put(datetime.now(), False)
                    # self.readlasttime = datetime.now()
                    try:
                        # 可以使用put_nowait，如果队列满了不会阻塞，但是会因为队列满了而报错
                        # 因此我们可以用一个try语句来处理这个错误。这样程序不会一直阻塞下去，但是会丢掉这个消息
                        q.put_nowait([instrumentid, datetime.now(), reportpath2.split('/')[2]])
                        # q.put_nowait(instrumentid)
                    except:
                        # 队列已经满了
                        pass
            try:
                if readlinenum % self.rand == 0:
                    time.sleep(0)
                QApplication.processEvents()
                if firstline:
                    firstline = False
                else:
                    mdarr = line.strip('\n').split(',')
                    thistime = datetime.strptime(str(mdarr[0]), "%Y-%m-%d %H:%M:%S")
                    TradingDay = int(thistime.strftime("%Y%m%d"))
                    klinetime = int(thistime.strftime("%H%M%S"))
                    kline = VNKlineData()
                    kline.TradingDay = TradingDay
                    kline.klinetime = klinetime
                    kline.open = float(mdarr[1])
                    kline.high = float(mdarr[2])
                    kline.low = float(mdarr[3])
                    kline.close = float(mdarr[4])
                    kline.volume = int(float(mdarr[5]))
                    kline.money = float(mdarr[6])
                    kline.open_interest = float(mdarr[7])
                    kline.InstrumentID = mdarr[8].encode('utf-8')
                    flist2 = [[1, 1],
                              [1, 1],
                              [1, 1],
                              [1, 1],
                              [1, 1],
                              [1, 1]]
                    ms.OnKline(reportpath1, reportpath2, kline, arg, strategyname, flist2)
                    # try:
                    # 动态权益    =  （1）可用资金          （2）保证金  （3）今日持仓盈亏
                    totalamount = ms.useramount + GetCX(mdarr[8])[0] * (
                            (float(ms.buyvol + ms.buyvol_history)) * ms.buyprice +
                            (float(ms.sellvol + ms.sellvol_history)) * ms.sellprice) + ms.todayPositionProfit

                    # except Exception as e:
                    #   print('totalamount error:', e)
                    if readlinenum % refreshfrequency == 0:
                        percent = '%.2f' % (float(100 * byteadd) / float(fileinfo.st_size))
                        print(str(arg) + '(' + percent + '%)amount:' + str(totalamount) + line)
                    self.GenerateBackTestEquityCurve(TradingDay, klinetime, totalamount)
                    self.WirteBackTestEquityCurve(reportpath1, reportpath2, arg)
                readlinenum += 1
            except:
                pass
        return [arg, ['%.2f' % totalamount, '%.2f' % (100.0 * (totalamount - ms.initamount) / ms.initamount),
                      '%.2f' % ms.winningrate, '%.2f' % ms.profitlossratio,
                      ms.buyopennum + ms.sellopennum + ms.sellclosenum + ms.buyclosenum, ms.sharperatio,
                      ms.sortino], reportpath2, instrumentid]

    # 每次回测从文件读取
    def StrategyCalculate_file(self, strategyname, mainstrategyname, reportpath1, reportpath2, arg,
                               splipoint, period, csvfile):
        module = self.dynamic_import('strategyfilebacktest.' + mainstrategyname)
        ms = module.MyStrategy(period, splipoint)
        firstline = True
        linenum = 0
        readlinenum = 0
        with open(csvfile, 'r') as f:
            for line in f:
                time.sleep(0)
                QApplication.processEvents()
                linenum += 1
        self.list_backtestfile = []
        with open(csvfile, 'r') as f:
            for line in f:

                if firstline:
                    firstline = False
                else:
                    mdarr = line.strip('\n').split(',')
                    thistime = datetime.strptime(str(mdarr[0]), "%Y-%m-%d %H:%M:%S")
                    TradingDay = int(thistime.strftime("%Y%m%d"))
                    klinetime = int(thistime.strftime("%H%M%S"))
                    kline = VNKlineData()
                    kline.TradingDay = TradingDay  # mdarr[0][0:9].encode('utf-8')
                    kline.klinetime = klinetime  # mdarr[0][11:18].encode('utf-8')
                    kline.open = float(mdarr[1])
                    kline.high = float(mdarr[2])
                    kline.low = float(mdarr[3])
                    kline.close = float(mdarr[4])
                    kline.volume = int(float(mdarr[5]))
                    kline.money = float(mdarr[6])
                    kline.open_interest = float(mdarr[7])
                    kline.InstrumentID = mdarr[8].encode('utf-8')
                    ms.OnKline(kline, arg, strategyname)

                self.GenerateBackTestEquityCurve(self.list_backtestfile, reportpath2, TradingDay, klinetime,
                                                 mainstrategyname, arg, ms.useramount,
                                                 ms.initamount)
                self.WirteBackTestEquityCurve(self.list_backtestfile, reportpath1, reportpath2, arg)
                readlinenum += 1

        return [arg, [ms.useramount, 100 * (ms.useramount - ms.initamount) / ms.initamount,
                      ms.buyopennum, ms.sellopennum, ms.buyclosenum, ms.sellclosenum], reportpath2]

    def StrategyCalculate_Virtualapi(self, arg):
        return [arg, arg[0] + arg[1] + arg[2] + arg[3], 2.3, 0.5]

    def StrategyCalculate(self, strategyname, mainstrategyname, reportpath1, reportpath2, arg, refreshfrequency,
                          slippoint,
                          period, csvfile, linenum, instrumentid, q, memory_datalist, diffms):
        if True:
            # python简单读取文件模式回测
            return self.StrategyCalculate_memory(strategyname, mainstrategyname, reportpath1, reportpath2, arg,
                                                 refreshfrequency,
                                                 slippoint, period, csvfile, linenum, instrumentid, q, memory_datalist,
                                                 diffms)
        elif True:
            return self.StrategyCalculate_file(strategyname, mainstrategyname, reportpath1, reportpath2, arg,
                                               splipoint, period, csvfile)
        else:
            # 仿真回测, 暂不支持
            return self.StrategyCalculate_Virtualapi(arg)


class BackTestUpdateResultThread(threading.Thread):
    def __del__(self):
        self.wait()

    def __init__(self):
        super(self).__init__()

    def run(self):
        time.sleep(1)
        globalvar.md.ui = globalvar.ui


class myThread(threading.Thread):
    def __init__(self, threadID, name, counter, threadLock):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
        self.threadLock = threadLock

    def run(self):
        print("开启线程： " + self.name)
        # 获取锁，用于线程同步
        self.threadLock.acquire()
        self.print_time(self.name, self.counter, 3)
        # 释放锁，开启下一个线程
        self.threadLock.release()

    def print_time(self, threadName, delay, counter):
        while counter:
            # time.sleep(delay)
            if threadName == 'Thread-1':
                # globalvar.vnfa.AsynSleep(random.randint(0, 9000))
                time.sleep(9)
            else:
                # globalvar.vnfa.AsynSleep(random.randint(0, 1000))
                time.sleep(1)
            QApplication.processEvents()
            print("%s: %s" % (threadName, time.ctime(time.time())))
            counter -= 1


class BackTestThreadMangement(object):
    # 开仓过滤规则1
    dict_strategyopenrule1 = {}
    # 平仓过滤规则1
    dict_strategycloserule1 = {}
    # 开仓过滤规则2
    dict_strategyopenrule2 = {}
    # 平仓过滤规则2
    dict_strategycloserule2 = {}
    # 开仓过滤规则3
    dict_strategyopenrule3 = {}
    # 平仓过滤规则3
    dict_strategycloserule3 = {}

    # 保存策略配置文件中过滤规则字段名称
    dict_session = {0: 'openrule1',
                    1: 'closerule1',
                    2: 'openrule2',
                    3: 'closerule2',
                    4: 'openrule3',
                    5: 'closerule3'}

    def __init__(self, processnum, refreshfrequency):
        super().__init__()
        self.processnum = processnum
        self.refreshfrequency = refreshfrequency
        # 进程进度条ID
        self.barid = 0
        self.memorydatalist = []

        self.lastinsurumentid = 0
        self.fileidadd = 0
        self.dict_p = {'M1': 1,
                       'M3': 3,
                       'M5': 5,
                       'M10': 10,
                       'M15': 15,
                       'M30': 30,
                       'M60': 60,
                       'M120': 120,
                       'D1': 9999, }

    # 动态加载策略或过滤规则
    def dynamic_import(self, module):
        return importlib.import_module(module)

    # 取最小值，但需忽略无效值0的影响
    # @nb.jit(nogil=True, nopython=True)
    def z_min(self, a, b):
        if a < 1e-7:
            return b
        elif b < 1e-7:
            return a
        else:
            return min(a, b)

    # 主力合约差值前复权,根据最后一个字段合约名称判断，通过收盘价和开盘价差，计算复权因子进行复权
    def AdjustmentPrice_Backward(self, filesize):
        firstline = True
        memorydatalist2 = []
        linenumadd = 0
        byteadd = 0
        instrumentID_last = ''
        closeprice_last = 0
        diffprice = 0
        for line in self.memorydatalist:
            if globalvar.DialogBackTestPoint.closestate:
                break
            time.sleep(0)
            QApplication.processEvents()
            if firstline:
                firstline = False
            else:
                mdarr = line.strip('\n').split(',')
                instrumentID = mdarr[8]
                if instrumentID_last != instrumentID:
                    diffprice = diffprice + float(mdarr[2]) - closeprice_last
                    combineddata = mdarr[0] + ',' + str(float(mdarr[1]) + diffprice) + ',' + str(
                        float(mdarr[2]) + diffprice) + ',' + str(float(mdarr[3]) + diffprice) + ',' + str(
                        float(mdarr[4]) + diffprice) + ',' + mdarr[5] + ',' + mdarr[6] + ',' + mdarr[7] + ',' + \
                                   mdarr[8]
                    memorydatalist2.append(combineddata)
                    closeprice_last = float(mdarr[4])
                    instrumentID_last = instrumentID
                else:
                    memorydatalist2.append(line)
            linenumadd += 1
            byteadd = byteadd + len(line) + 1
            globalvar.DialogBackTestPoint.bt.signal_backtest_adjustmentprice.emit(
                [linenumadd, byteadd / filesize, '后复权'])
        del self.memorydatalist[:]
        self.memorydatalist = copy.deepcopy(memorydatalist2)

    # 主力合约差值后复权
    def AdjustmentPrice_Forward(self, filesize):
        firstline = True
        memorydatalist2 = []
        linenumadd = 0
        byteadd = 0
        instrumentID_last = ''
        closeprice_last = 0
        diffprice = 0
        for line in self.memorydatalist:
            if globalvar.DialogBackTestPoint.closestate:
                break
            time.sleep(0)
            QApplication.processEvents()
            if firstline:
                firstline = False
            else:
                mdarr = line.strip('\n').split(',')
                instrumentID = mdarr[8]
                if instrumentID_last != instrumentID:
                    diffprice = diffprice + float(mdarr[2]) - closeprice_last
                    combineddata = mdarr[0] + ',' + str(float(mdarr[1]) + diffprice) + ',' + str(
                        float(mdarr[2]) + diffprice) + ',' + str(float(mdarr[3]) + diffprice) + ',' + str(
                        float(mdarr[4]) + diffprice) + ',' + mdarr[5] + ',' + mdarr[6] + ',' + mdarr[7] + ',' + \
                                   mdarr[8]
                    memorydatalist2.append(combineddata)
                    closeprice_last = float(mdarr[4])
                    instrumentID_last = instrumentID
                else:
                    memorydatalist2.append(line)
            linenumadd += 1
            byteadd = byteadd + len(line) + 1
            globalvar.DialogBackTestPoint.bt.signal_backtest_adjustmentprice.emit(
                [linenumadd, byteadd / filesize, '前复权'])
        del self.memorydatalist[:]
        self.memorydatalist = copy.deepcopy(memorydatalist2)

    # 合并各周期K线数据
    def CombinedKlineData(self, period, filesize):
        firstline = True
        kline = VNKlineData()
        memorydatalist2 = []
        linenumadd = 0
        byteadd = 0
        for line in self.memorydatalist:
            if globalvar.DialogBackTestPoint.closestate:
                break
            time.sleep(0)
            QApplication.processEvents()
            if firstline:
                firstline = False
            else:
                mdarr = line.strip('\n').split(',')
                thistime = datetime.strptime(str(mdarr[0]), "%Y-%m-%d %H:%M:%S")
                TradingDay = int(thistime.strftime("%Y%m%d"))
                klinetime = int(thistime.strftime("%H%M%S"))
                minute = int(thistime.strftime("%M"))
                if minute % self.dict_p[period] == 0:
                    combineddata = str(mdarr[0]) + ',' + \
                                   str(kline.open) + ',' + \
                                   str(kline.high) + ',' + \
                                   str(kline.low) + ',' + \
                                   str(kline.close) + ',' + \
                                   str(kline.volume) + ',' + \
                                   str(kline.money) + ',' + \
                                   str(kline.open_interest) + ',' + \
                                   str(kline.InstrumentID.decode())
                    memorydatalist2.append(combineddata)
                    kline = VNKlineData()
                    kline.TradingDay = TradingDay
                    kline.klinetime = klinetime
                    kline.open = float(mdarr[1])
                    kline.high = max(kline.high, float(mdarr[2]))
                    kline.low = self.z_min(kline.low, float(mdarr[3]))
                    kline.close = float(mdarr[4])
                    kline.volume = kline.volume + int(float(mdarr[5]))
                    kline.money = kline.money + float(mdarr[6])
                    kline.open_interest = float(mdarr[7])
                    kline.InstrumentID = mdarr[8].encode('utf-8')
                else:
                    kline.TradingDay = TradingDay
                    kline.klinetime = klinetime
                    kline.open = float(mdarr[1])
                    kline.high = max(kline.high, float(mdarr[2]))
                    kline.low = self.z_min(kline.low, float(mdarr[3]))
                    kline.close = float(mdarr[4])
                    kline.volume = kline.volume + int(float(mdarr[5]))
                    kline.money = kline.money + float(mdarr[6])
                    kline.open_interest = float(mdarr[7])
                    kline.InstrumentID = mdarr[8].encode('utf-8')
            linenumadd += 1
            byteadd = byteadd + len(line) + 1
            # if linenumadd%10==0:
            globalvar.DialogBackTestPoint.bt.signal_backtest_loaddata.emit(
                [2, linenumadd, float(byteadd) / float(filesize), period])
        self.memory_datalist = copy.deepcopy(memorydatalist2)

    def BackTestProcess(self, strategyname, mainstrategyname, reportpath1, reportpath2, arg, refreshfrequency,
                        slippoint, period,
                        csvfile, linenum, instrumentid, q, memory_datalist, diffms):
        time.sleep(0)
        QApplication.processEvents()
        sc = StrategyCalculate(self)
        result = sc.StrategyCalculate(strategyname, mainstrategyname, reportpath1, reportpath2, arg,
                                      refreshfrequency, slippoint,
                                      period, csvfile, linenum, instrumentid, q, memory_datalist, diffms)
        diffsecond = (datetime.now() - self.starttime).seconds
        print('进程: %s' % (result))
        return [result, diffsecond, os.getpid()]

    # @nb.jit(nogil=True, nopython=True)
    def waitsleep(self):
        time.sleep(0)
        QApplication.processEvents()

    def RunAgain(self, arg):
        instrumentid = arg[0]
        now = arg[1]
        print('----RunAgain[%s,%s]----' % (instrumentid, self.lastinsurumentid))
        try:
            if instrumentid == self.lastinsurumentid:
                globalvar.BackTestThreadPoint.OnStop('完成回测')
                globalvar.DialogBackTestPoint.OnCloseProcesswindow()
                globalvar.backteststate = 2
            else:
                print('下一个合约')
                unfinish = False
                for key in globalvar.BackTestThreadPoint.pardict.keys():
                    if globalvar.BackTestThreadPoint.pardict[key]:
                        continue
                    unfinish = True
                if unfinish:
                    self.RunBackTestAgain(self.refreshfrequency, self.adjustment, self.slippoint,
                                          self.period, instrumentid, now)
                else:
                    globalvar.DialogBackTestPoint.OnCloseProcesswindow()
                    globalvar.pool.close()
                    globalvar.pool.terminate()
                    globalvar.BackTestThreadPoint.OnStopAndNext(instrumentid + 1)
        except Exception as e:
            print('module_backtest.RunAgain error:', e)

    def CallRusult(self, result):
        t1 = str(result[0][0][0]) + ',' + str(result[0][0][1]) + ',' + str(result[0][0][2]) + ',' + str(
            result[0][0][3]) + ',' + str(result[0][0][4]) + ',' + str(result[0][0][5])
        t2 = str(result[0][1][0]) + ',' + str(result[0][1][1]) + ',' + str(result[0][1][2]) + ',' + str(
            result[0][1][3]) + ',' + str(result[0][1][4]) + ',' + str(result[0][1][5]) + ',' + str(result[0][1][6])
        globalvar.BackTestThreadPoint.pardict[t1] = True
        # 进程: [[3, 15, 0, 0, 0, 0], [452372.0, -9.5256, 592, 592, 592, 582],'backtestreport/talib_MA/202112250620/rb9999.XSGE.csv', 0]

        globalvar.finishtasknum += 1
        globalvar.DialogBackTestPoint.bt.signal_backtest_result.emit(result)
        list = result[0][2].split('/')
        # print('list: '+str(list))

        indexpath = list[0] + '/' + list[1] + '/' + list[2] + '/' + 'resultlist.csv'
        with open('%s' % (indexpath),
                  "a") as file:
            file.write(t1 + ',' + t2 + '\n')
        if globalvar.finishtasknum == globalvar.totaltasknum:
            if globalvar.DialogBackTestPoint.closestate == False:
                globalvar.DialogBackTestPoint.closestate = True
                globalvar.DialogBackTestPoint.backteststate = 1
                globalvar.DialogBackTestPoint.table_lefttop.setEditTriggers(QTableView.CurrentChanged)
                # print(str(globalvar.totaltasknum))
                globalvar.DialogBackTestPoint.bt.signal_backtest_addresult.emit(result)
                print('result: ' + str(result))
                # globalvar.pool.terminate()

    def CleanDir(self, dir):
        if os.path.isdir(dir):
            paths = os.listdir(dir)
            for path in paths:
                filePath = os.path.join(dir, path)
                if os.path.isfile(filePath):
                    try:
                        os.remove(filePath)
                    except os.error:
                        print("remove %s error." % filePath)  # 引入logging
                elif os.path.isdir(filePath):
                    if filePath[-4:].lower() == ".svn".lower():
                        continue
                    shutil.rmtree(filePath, True)
        return True

    # 读数据进程执行的代码：
    def ProcessWinThread(self):
        try:
            app = module_backtestwindow.QtWidgets.QApplication(sys.argv)
            # app.aboutToQuit.connect(app.deleteLater)
            app.setStyleSheet(module_backtestwindow.qdarkstyle.load_stylesheet_pyqt5())
            globalvar.gui = module_backtestwindow.MainUi(200)
            # gui.move(50, 50)
            # gui.genMastClicked(msg)
            globalvar.gui.desktop = QDesktopWidget()
            globalvar.gui.move((globalvar.gui.desktop.availableGeometry().width() - globalvar.gui.width()),
                               globalvar.gui.desktop.availableGeometry().height() - globalvar.gui.height() - 10)  # 初始化位置到右下角
            # globalvar.gui.show()
            while globalvar.printwinstate:
                time.sleep(0)
                QApplication.processEvents()
            os._exit(1)

            sys.exit(app.exec_())
        except Exception as e:
            print('PrintWin error:', e)

    def _async_raise(self, tid, exctype):
        """raises the exception, performs cleanup if needed"""
        if not inspect.isclass(exctype):
            raise TypeError("Only types can be raised (not instances)")
        res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
        if res == 0:
            raise ValueError("invalid thread id")
        elif res != 1:
            # """if it returns a number greater than one, you're in trouble,
            # and you should call it again with exc=NULL to revert the effect"""
            ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
            raise SystemError("PyThreadState_SetAsyncExc failed")

    def ExitProcessWinThread(self, win, pipe0, pipe1):
        try:
            pipe0.close()
            str = pipe1.recv()
            if str == 'hide':
                self._async_raise(win.ident, SystemExit)
                # globalvar.printwinstate =False
                # win.setVisible(False)
        except EOFError:
            pass

    # @nb.jit(nogil=True, nopython=True)
    def PrintWin(self, pipe0, pipe1):
        globalvar.printwinstate = True
        win = threading.Thread(target=self.ProcessWinThread, args=(), name='PrintWin')
        win.daemon = True
        win.start()
        endwin = threading.Thread(target=self.ExitProcessWinThread, args=(win, pipe0, pipe1,),
                                  name='ExitProcessWinThread')
        endwin.daemon = True
        endwin.start()

    # globalvar.DialogBackTestPoint.OnCloseProcesswindow()
    def CheckFinish(self, q):
        num = 0
        lastinstrumentid = ''
        listq = []
        timepath = ''
        while True:
            try:
                QApplication.processEvents()
                if q.empty():
                    if len(listq) > 0:
                        diffsecond = (datetime.now() - listq[1]).seconds
                        if diffsecond > 10:
                            # globalvar.DialogBackTestPoint.OnCloseProcesswindow()
                            return [lastinstrumentid, timepath]
                    time.sleep(0.3)
                else:
                    listq = q.get(True)
                    lastinstrumentid = listq[0]
                    timepath = listq[2]

            except Exception as e:
                print('CheckFinish error:', e)
                if str(e) == '[WinError 232] 管道正在被关闭。':
                    num += 1
                    if num > 10:
                        print("执行完成2")
                        return [lastinstrumentid, timepath]

    # 保存本次回测过滤规则设置到回测结果文件目录
    def SaveFilterConfig(self, pathstr, strategyname):
        try:
            source = (globalvar.currpath + '/strategyfile/' + strategyname + ".ini").replace("\\", "/")
            target = (globalvar.currpath + "/" + pathstr + '/' + strategyname + '.ini').replace("\\", "/")
            try:
                shutil.copy(source, target)
                try:
                    for i in range(len(self.dict_strategyopenrule1)):
                        source = (globalvar.currpath + '/strategyfilterfile/' + self.dict_strategyopenrule1[i])
                        target = (globalvar.currpath + "/" + pathstr + '/' + self.dict_strategyopenrule1[i])
                        shutil.copy(source, target)
                except IOError as e:
                    print("module_backtest.SaveFilterConfig1 %s" % e)
                try:
                    for i in range(len(self.dict_strategycloserule1)):
                        source = (globalvar.currpath + '/strategyfilterfile/' + self.dict_strategycloserule1[i])
                        target = (globalvar.currpath + "/" + pathstr + '/' + self.dict_strategycloserule1[i])
                        shutil.copy(source, target)
                except IOError as e:
                    print("module_backtest.SaveFilterConfig2 %s" % e)

                try:
                    for i in range(len(self.dict_strategyopenrule2)):
                        source = (globalvar.currpath + '/strategyfilterfile/' + self.dict_strategyopenrule2[i])
                        target = (globalvar.currpath + "/" + pathstr + '/' + self.dict_strategyopenrule2[i])
                        shutil.copy(source, target)
                except IOError as e:
                    print("module_backtest.SaveFilterConfig3 %s" % e)

                try:
                    for i in range(len(self.dict_strategycloserule2)):
                        source = (globalvar.currpath + '/strategyfilterfile/' + self.dict_strategycloserule2[i])
                        target = (globalvar.currpath + "/" + pathstr + '/' + self.dict_strategycloserule2[i])
                        shutil.copy(source, target)
                except IOError as e:
                    print("module_backtest.SaveFilterConfig4 %s" % e)

                try:
                    for i in range(len(self.dict_strategyopenrule3)):
                        source = (globalvar.currpath + '/strategyfilterfile/' + self.dict_strategyopenrule3[i])
                        target = (globalvar.currpath + "/" + pathstr + '/' + self.dict_strategyopenrule3[i])
                        shutil.copy(source, target)
                except IOError as e:
                    print("module_backtest.SaveFilterConfig5 %s" % e)

                try:
                    for i in range(len(self.dict_strategycloserule3)):
                        source = (globalvar.currpath + '/strategyfilterfile/' + self.dict_strategycloserule3[i])
                        target = (globalvar.currpath + "/" + pathstr + '/' + self.dict_strategycloserule3[i])
                        shutil.copy(source, target)
                except IOError as e:
                    print("module_backtest.SaveFilterConfig6 %s" % e)
            except IOError as e:
                print("module_backtest.SaveFilterConfig %s" % e)
        except Exception as e:
            print("module_backtest.SaveFilterConfig %s" % e)

    # 运行回测
    def RunBackTest(self, refreshfrequency, adjustment, slippoint, period, fileid):
        self.refreshfrequency = refreshfrequency
        self.adjustment = adjustment
        self.slippoint = slippoint
        self.period = period
        fileid = fileid + self.fileidadd
        self.fileid = fileid
        # try:
        self.starttime = datetime.now()
        timepath = time.strftime("%Y%m%d%H%M", time.localtime())
        try:
            globalvar.pool = multiprocessing.Pool(self.processnum + 1)

            # multiprocessing.set_start_method('spawn')
            globalvar.manager = multiprocessing.Manager()
            # 父进程创建Queue，并传给各个子进程：
            globalvar.q = globalvar.manager.Queue(maxsize=20)
            globalvar.pipe0, globalvar.pipe1 = Pipe()
            if True:
                globalvar.pool.apply_async(self.CheckFinish, args=(globalvar.q,), callback=self.RunAgain)
                globalvar.pool.apply_async(self.PrintWin, args=(globalvar.pipe0, globalvar.pipe1,))
                self.lastinsurumentid = len(globalvar.DialogBackTestPoint.csvfile) - 1

                # for i in range(self.lastinsurumentid + 1):
                if fileid > self.lastinsurumentid:
                    return
                reportpath1 = 'backtestreport/%s' % (globalvar.DialogBackTestPoint.mainstrategyname)
                reportpath2 = 'backtestreport/%s/%s/%s' % (
                    globalvar.DialogBackTestPoint.mainstrategyname, timepath,
                    globalvar.DialogBackTestPoint.csvfile[fileid])
                deletepath = 'backtestreport/%s/%s' % (globalvar.DialogBackTestPoint.mainstrategyname, timepath)
                # print("@@reportpath1:" + reportpath1)
                # print("@@reportpath2:" + reportpath2) xxx
                # print("@@deletepath:" + deletepath)
                # @@reportpath1:backtestreport/talib_MA
                # @@reportpath2:backtestreport/talib_MA/202204051531/BC9999.XINE.csv
                # @@deletepath:backtestreport/talib_MA/202204051531

                if Path(deletepath).is_dir():
                    self.CleanDir(deletepath)
                self.memorydatalist = []
                globalvar.csvfile = 'backtestdata/' + globalvar.DialogBackTestPoint.csvfile[fileid]
                linenum = len(self.memorydatalist)
                fileinfo = os.stat(globalvar.csvfile)
                print('csvfile:' + globalvar.csvfile)
                print('fileinfo size: ' + str(fileinfo.st_size))
                byteadd = 0
                linenumadd = 0
                if linenum == 0:
                    with open(globalvar.csvfile, 'r') as f:
                        for line in f:
                            if globalvar.DialogBackTestPoint.closestate:
                                break
                            time.sleep(0)
                            QApplication.processEvents()
                            self.memorydatalist.append(line)
                            linenumadd += 1
                            byteadd = byteadd + len(line) + 1
                            # if linenumadd%100==0:
                            globalvar.DialogBackTestPoint.bt.signal_backtest_loaddata.emit(
                                [1, linenumadd, float(byteadd) / float(fileinfo.st_size),
                                 globalvar.DialogBackTestPoint.csvfile[fileid]])
                            # print('linenumadd: %d ,%d,%d, %d, %.2f'%(linenumadd,sys.getsizeof(line), byteadd,fileinfo.st_size,float(byteadd) / float(fileinfo.st_size)))

                    linenum = len(self.memorydatalist)
                    if adjustment == '后复权':
                        self.AdjustmentPrice_Backward(fileinfo.st_size)
                    elif adjustment == '前复权':
                        self.AdjustmentPrice_Forward(fileinfo.st_size)
                    # 合并周期
                    self.memory_datalist = multiprocessing.Manager().list()
                    if period != "M1":
                        globalvar.DialogBackTestPoint.Log('开始合并周期数据')
                        diffms = multiprocessing.Manager().Value(ctypes.c_float, 0)
                        self.CombinedKlineData(period, fileinfo.st_size)
                        globalvar.DialogBackTestPoint.Log('完成合并周期数据')
                    else:
                        self.memory_datalist = copy.deepcopy(self.memorydatalist)

                    del self.memorydatalist[:]
                for key in globalvar.BackTestThreadPoint.pardict.keys():
                    if globalvar.BackTestThreadPoint.pardict[key]:
                        continue
                    parstr = key.split(',')
                    par = [int(parstr[0]), int(parstr[1]), int(parstr[2]), int(parstr[3]), int(parstr[4]),
                           int(parstr[5])]
                    self.waitsleep()
                    if globalvar.DialogBackTestPoint.closestate:
                        break
                    else:
                        try:
                            globalvar.pool.apply_async(self.BackTestProcess, args=(
                                globalvar.DialogBackTestPoint.strategyname,
                                globalvar.DialogBackTestPoint.mainstrategyname,
                                reportpath1, reportpath2, par, refreshfrequency, slippoint, period,
                                globalvar.csvfile, linenum, fileid, globalvar.q, self.memory_datalist, diffms,),
                                                       callback=self.CallRusult)
                        except Exception as e:
                            print('RunBackTest error:', e)

                self.SaveFilterConfig(deletepath, globalvar.DialogBackTestPoint.mainstrategyname)
                globalvar.pool.close()
                globalvar.pool.join()
                del self.memory_datalist[:]
        except Exception as e:
            print('RunBackTest error:', e)

    # 第二次回测
    def RunBackTestAgain(self, refreshfrequency, adjustment, slippoint, period, fileid, timepath):
        self.refreshfrequency = refreshfrequency
        self.adjustment = adjustment
        self.slippoint = slippoint
        self.period = period
        fileid = fileid + self.fileidadd
        self.fileid = fileid
        # try:
        self.starttime = datetime.now()
        try:
            globalvar.pool = multiprocessing.Pool(self.processnum + 1)
            globalvar.manager = multiprocessing.Manager()
            # 父进程创建Queue，并传给各个子进程：
            globalvar.q = globalvar.manager.Queue(maxsize=20)
            globalvar.pipe0, globalvar.pipe1 = Pipe()
            globalvar.pool.apply_async(self.CheckFinish, args=(globalvar.q,), callback=self.RunAgain)
            globalvar.pool.apply_async(self.PrintWin, args=(globalvar.pipe0, globalvar.pipe1,))

            self.lastinsurumentid = len(globalvar.DialogBackTestPoint.csvfile) - 1
            if True:
                # for i in range(self.lastinsurumentid + 1):
                if fileid > self.lastinsurumentid:
                    return
                reportpath1 = 'backtestreport/%s' % (globalvar.DialogBackTestPoint.mainstrategyname)
                reportpath2 = 'backtestreport/%s/%s/%s' % (
                    globalvar.DialogBackTestPoint.mainstrategyname, timepath,
                    globalvar.DialogBackTestPoint.csvfile[fileid])
                self.memorydatalist = []
                csvfile = 'backtestdata/' + globalvar.DialogBackTestPoint.csvfile[fileid]
                linenum = len(self.memorydatalist)
                fileinfo = os.stat(csvfile)
                byteadd = 0
                linenumadd = 0
                if linenum == 0:
                    with open(csvfile, 'r') as f:
                        for line in f:
                            if globalvar.DialogBackTestPoint.closestate:
                                break
                            time.sleep(0)
                            QApplication.processEvents()
                            self.memorydatalist.append(line)
                            linenumadd += 1
                            byteadd = byteadd + len(line) + 1
                            # if linenumadd%100==0:
                            globalvar.DialogBackTestPoint.bt.signal_backtest_loaddata.emit(
                                [1, linenumadd, float(byteadd) / float(fileinfo.st_size),
                                 globalvar.DialogBackTestPoint.csvfile[fileid]])
                    linenum = len(self.memorydatalist)
                    if adjustment == '后复权':
                        self.AdjustmentPrice_Backward(fileinfo.st_size)
                    elif adjustment == '前复权':
                        self.AdjustmentPrice_Forward(fileinfo.st_size)
                    # 合并周期
                    self.memory_datalist = multiprocessing.Manager().list()
                    if period != "M1":
                        globalvar.DialogBackTestPoint.Log('开始合并周期数据')
                        self.CombinedKlineData(period, fileinfo.st_size)
                        globalvar.DialogBackTestPoint.Log('完成合并周期数据')
                    else:
                        self.memory_datalist = copy.deepcopy(self.memorydatalist)
                    del self.memorydatalist[:]

                self.fileidadd = 1
                for key in globalvar.BackTestThreadPoint.pardict.keys():
                    if globalvar.BackTestThreadPoint.pardict[key]:
                        continue
                    self.fileidadd = 0
                    parstr = key.split(',')
                    par = [int(parstr[0]), int(parstr[1]), int(parstr[2]), int(parstr[3]), int(parstr[4]),
                           int(parstr[5])]
                    self.waitsleep()
                    if globalvar.DialogBackTestPoint.closestate:
                        break
                    else:
                        try:
                            globalvar.pool.apply_async(self.BackTestProcess, args=(
                                globalvar.DialogBackTestPoint.strategyname,
                                globalvar.DialogBackTestPoint.mainstrategyname,
                                reportpath1, reportpath2, par, refreshfrequency, slippoint, period,
                                csvfile, linenum, fileid, globalvar.q,), callback=self.CallRusult)
                        except Exception as e:
                            print('RunBackTest error:', e)

            globalvar.pool.close()
            globalvar.pool.join()
            del self.memory_datalist[:]
        except Exception as e:
            print('RunBackTestAgain error:', e)


# 量化回测线程
class BackTestThread(QtCore.QThread):
    signal_backtest_processbar = pyqtSignal(list)
    signal_backtest_result = pyqtSignal(list)
    signal_backtest_loaddata = pyqtSignal(list)
    signal_backtest_addresult = pyqtSignal(list)
    signal_backtest_adjustmentprice = pyqtSignal(list)

    def __del__(self):
        self.wait()

    def __init__(self):
        super(BackTestThread, self).__init__()
        globalvar.BackTestThreadPoint = self
        # 存储所有参数组合
        self.pardict = {}
        globalvar.DialogBackTestPoint.closestate = False
        self.starttime = 0

    def run(self):
        pass

    # 初始化回测
    def Initprocess(self, processnum, refreshfrequency, adjustment, slippoint, period, fileid):
        bt = BackTestThreadMangement(processnum, refreshfrequency)
        bt.RunBackTest(refreshfrequency, adjustment, slippoint, period, fileid)

    # 根据过滤条件，过滤参数组，减少不必要的回测，加快回测进度
    def FilterPar2(self, parnum, conditions0, conditions1, conditions2, value1, value2, value3, value4, value5, value6):
        if parnum == 0 or parnum == 1:
            return True
        elif parnum == 2:
            if conditions0 == '' or conditions0 == '参数条件：不设置':
                return True
            elif conditions0 == '参数条件：参数1<参数2':
                if value1 < value2:
                    return True
                else:
                    return False
            elif conditions0 == '参数条件：参数1>参数2':
                if value1 > value2:
                    return True
                else:
                    return False
        elif parnum == 3:
            add = 0
            if conditions0 == '' or conditions0 == '参数条件：不设置':
                add += 1
            elif conditions0 == '参数条件：参数1<参数2':
                if value1 < value2:
                    add += 1
            elif conditions0 == '参数条件：参数1>参数2':
                if value1 > value2:
                    add += 1
            elif conditions0 == '参数条件：参数1<参数3':
                if value1 < value3:
                    add += 1
            elif conditions0 == '参数条件：参数1>参数3':
                if value1 > value3:
                    add += 1
            elif conditions0 == '参数条件：参数2<参数3':
                if value2 < value3:
                    add += 1
            elif conditions0 == '参数条件：参数2>参数3':
                if value2 > value3:
                    add += 1

            if conditions1 == '' or conditions1 == '参数条件：不设置':
                add += 1
            elif conditions1 == '参数条件：参数1<参数2':
                if value1 < value2:
                    add += 1
            elif conditions1 == '参数条件：参数1>参数2':
                if value1 > value2:
                    add += 1
            elif conditions1 == '参数条件：参数1<参数3':
                if value1 < value3:
                    add += 1
            elif conditions1 == '参数条件：参数1>参数3':
                if value1 > value3:
                    add += 1
            elif conditions1 == '参数条件：参数2<参数3':
                if value2 < value3:
                    add += 1
            elif conditions1 == '参数条件：参数2>参数3':
                if value2 > value3:
                    add += 1
            if add == 2:
                return True
            else:
                return False

        elif parnum >= 4:
            add = 0
            if conditions0 == '' or conditions0 == '参数条件：不设置':
                add += 1
            elif conditions0 == '参数条件：参数1<参数2':
                if value1 < value2:
                    add += 1
            elif conditions0 == '参数条件：参数1>参数2':
                if value1 > value2:
                    add += 1
            elif conditions0 == '参数条件：参数1<参数3':
                if value1 < value3:
                    add += 1
            elif conditions0 == '参数条件：参数1>参数3':
                if value1 > value3:
                    add += 1
            elif conditions0 == '参数条件：参数1<参数4':
                if value1 < value4:
                    add += 1
            elif conditions0 == '参数条件：参数1>参数4':
                if value1 > value4:
                    add += 1
            elif conditions0 == '参数条件：参数2<参数3':
                if value2 < value3:
                    add += 1
            elif conditions0 == '参数条件：参数2>参数3':
                if value2 > value3:
                    add += 1
            elif conditions0 == '参数条件：参数2<参数4':
                if value2 < value4:
                    add += 1
            elif conditions0 == '参数条件：参数2>参数4':
                if value2 > value4:
                    add += 1
            elif conditions0 == '参数条件：参数3<参数4':
                if value3 < value4:
                    add += 1
            elif conditions0 == '参数条件：参数3>参数4':
                if value3 > value4:
                    add += 1

            if conditions1 == '' and conditions1 == '参数条件：不设置':
                add += 1
            elif conditions1 == '参数条件：参数1<参数2':
                if value1 < value2:
                    add += 1
            elif conditions1 == '参数条件：参数1>参数2':
                if value1 > value2:
                    add += 1
            elif conditions1 == '参数条件：参数1<参数3':
                if value1 < value3:
                    add += 1
            elif conditions1 == '参数条件：参数1>参数3':
                if value1 > value3:
                    add += 1
            elif conditions1 == '参数条件：参数1<参数4':
                if value1 < value4:
                    add += 1
            elif conditions1 == '参数条件：参数1>参数4':
                if value1 > value4:
                    add += 1
            elif conditions1 == '参数条件：参数2<参数3':
                if value2 < value3:
                    add += 1
            elif conditions1 == '参数条件：参数2>参数3':
                if value2 > value3:
                    add += 1
            elif conditions1 == '参数条件：参数2<参数4':
                if value2 < value4:
                    add += 1
            elif conditions1 == '参数条件：参数2>参数4':
                if value2 > value4:
                    add += 1
            elif conditions1 == '参数条件：参数3<参数4':
                if value3 < value4:
                    add += 1
            elif conditions1 == '参数条件：参数3>参数4':
                if value3 > value4:
                    add += 1

            if conditions2 == '' and conditions2 == '参数条件：不设置':
                add += 1
            elif conditions2 == '参数条件：参数1<参数2':
                if value1 < value2:
                    add += 1
            elif conditions2 == '参数条件：参数1>参数2':
                if value1 > value2:
                    add += 1
            elif conditions2 == '参数条件：参数1<参数3':
                if value1 < value3:
                    add += 1
            elif conditions2 == '参数条件：参数1>参数3':
                if value1 > value3:
                    add += 1
            elif conditions2 == '参数条件：参数1<参数4':
                if value1 < value4:
                    add += 1
            elif conditions2 == '参数条件：参数1>参数4':
                if value1 > value4:
                    add += 1
            elif conditions2 == '参数条件：参数2<参数3':
                if value2 < value3:
                    add += 1
            elif conditions2 == '参数条件：参数2>参数3':
                if value2 > value3:
                    add += 1
            elif conditions2 == '参数条件：参数2<参数4':
                if value2 < value4:
                    add += 1
            elif conditions2 == '参数条件：参数2>参数4':
                if value2 > value4:
                    add += 1
            elif conditions2 == '参数条件：参数3<参数4':
                if value3 < value4:
                    add += 1
            elif conditions2 == '参数条件：参数3>参数4':
                if value3 > value4:
                    add += 1
            if add == 3:
                return True
            else:
                return False

    # 过滤回测参数
    def FilterPar(self, parnum, conditions0, conditions1, conditions2, value1, value2, value3, value4, value5, value6):
        returnvalue = self.FilterPar2(parnum, conditions0, conditions1, conditions2, value1, value2, value3, value4,
                                      value5, value6)
        if returnvalue:
            globalvar.totaltasknum += 1
        return returnvalue

    # 开始量化回测
    def OnStart(self, parnum, refreshfrequency, adjustment, conditions0, conditions1, conditions2):
        globalvar.DialogBackTestPoint.backteststate = 1
        globalvar.DialogBackTestPoint.table_lefttop.setEditTriggers(QTableView.NoEditTriggers)
        globalvar.DialogBackTestPoint.btn_ok.setStyleSheet("QPushButton{border-image: url(onstopbacktest1.png)}")
        globalvar.DialogBackTestPoint.btn_ok.setFixedSize(256, 70)
        globalvar.DialogBackTestPoint.list_backtestlog.clear()
        globalvar.DialogBackTestPoint.taskprogressBar.setProperty("value", 0)
        globalvar.DialogBackTestPoint.taskprogressBar.setFormat("参数组回测任务进度 0%")
        globalvar.DialogBackTestPoint.Log("开始测试【" + globalvar.DialogBackTestPoint.strategyname + '】')
        for i in range(globalvar.DialogBackTestPoint.table_leftbottom.rowCount()):
            if globalvar.DialogBackTestPoint.table_leftbottom.item(i, 1).checkState():
                globalvar.DialogBackTestPoint.Log(
                    '将回测：' + globalvar.DialogBackTestPoint.table_leftbottom.item(i, 1).text())

        parnumlist = [0, 0, 0, 0, 0, 0]
        for rowid in range(parnum):
            begin = int(globalvar.DialogBackTestPoint.table_lefttop.item(rowid, 1).text())
            end = int(globalvar.DialogBackTestPoint.table_lefttop.item(rowid, 2).text())
            step = int(globalvar.DialogBackTestPoint.table_lefttop.item(rowid, 3).text())
            parnumlist[rowid] = (end - begin + 1) / max(1, step)
        globalvar.totaltasknum = 0
        globalvar.finishtasknum = 0
        self.pardict.clear()
        self.pardict = {}

        slippoint = globalvar.DialogBackTestPoint.comBox_slippoint.currentIndex() + 1
        period = globalvar.DialogBackTestPoint.comBox_period.currentText()
        if globalvar.DialogBackTestPoint.table_lefttop.item(0, 1):
            for parnumlist[0] in range(int(globalvar.DialogBackTestPoint.table_lefttop.item(0, 1).text()),
                                       int(globalvar.DialogBackTestPoint.table_lefttop.item(0, 2).text()) + 1,
                                       int(globalvar.DialogBackTestPoint.table_lefttop.item(0, 3).text())):
                if globalvar.DialogBackTestPoint.table_lefttop.item(1, 1):
                    for parnumlist[1] in range(int(globalvar.DialogBackTestPoint.table_lefttop.item(1, 1).text()),
                                               int(globalvar.DialogBackTestPoint.table_lefttop.item(1, 2).text()) + 1,
                                               int(globalvar.DialogBackTestPoint.table_lefttop.item(1, 3).text())):
                        if globalvar.DialogBackTestPoint.table_lefttop.item(2, 1):
                            for parnumlist[2] in range(
                                    int(globalvar.DialogBackTestPoint.table_lefttop.item(2, 1).text()),
                                    int(globalvar.DialogBackTestPoint.table_lefttop.item(2, 2).text()) + 1,
                                    int(globalvar.DialogBackTestPoint.table_lefttop.item(2, 3).text())):
                                if globalvar.DialogBackTestPoint.table_lefttop.item(3, 1):
                                    for parnumlist[3] in range(
                                            int(globalvar.DialogBackTestPoint.table_lefttop.item(3, 1).text()),
                                            int(globalvar.DialogBackTestPoint.table_lefttop.item(3, 2).text()) + 1,
                                            int(globalvar.DialogBackTestPoint.table_lefttop.item(3, 3).text())):
                                        if globalvar.DialogBackTestPoint.table_lefttop.item(4, 1):
                                            for parnumlist[4] in range(
                                                    int(globalvar.DialogBackTestPoint.table_lefttop.item(4, 1).text()),
                                                    int(globalvar.DialogBackTestPoint.table_lefttop.item(4,
                                                                                                         2).text()) + 1,
                                                    int(globalvar.DialogBackTestPoint.table_lefttop.item(4, 3).text())):
                                                if globalvar.DialogBackTestPoint.table_lefttop.item(5, 1):
                                                    for parnumlist[5] in range(
                                                            int(globalvar.DialogBackTestPoint.table_lefttop.item(5,
                                                                                                                 1).text()),
                                                            int(globalvar.DialogBackTestPoint.table_lefttop.item(5,
                                                                                                                 2).text()) + 1,
                                                            int(globalvar.DialogBackTestPoint.table_lefttop.item(5,
                                                                                                                 3).text())):
                                                        if self.FilterPar(parnum, conditions0, conditions1, conditions2,
                                                                          parnumlist[0],
                                                                          parnumlist[1], parnumlist[2], parnumlist[3],
                                                                          parnumlist[4], parnumlist[5]):
                                                            self.pardict['%d,%d,%d,%d,%d,%d' % (
                                                                parnumlist[0], parnumlist[1], parnumlist[2],
                                                                parnumlist[3],
                                                                parnumlist[4], parnumlist[5])] = False
                                                else:
                                                    if self.FilterPar(parnum, conditions0, conditions1, conditions2,
                                                                      parnumlist[0],
                                                                      parnumlist[1], parnumlist[2], parnumlist[3],
                                                                      parnumlist[4], parnumlist[5]):
                                                        self.pardict['%d,%d,%d,%d,%d,%d' % (
                                                            parnumlist[0], parnumlist[1], parnumlist[2],
                                                            parnumlist[3],
                                                            parnumlist[4], parnumlist[5])] = False
                                        else:
                                            if self.FilterPar(parnum, conditions0, conditions1, conditions2,
                                                              parnumlist[0],
                                                              parnumlist[1], parnumlist[2], parnumlist[3],
                                                              parnumlist[4], parnumlist[5]):
                                                self.pardict['%d,%d,%d,%d,%d,%d' % (
                                                    parnumlist[0], parnumlist[1], parnumlist[2],
                                                    parnumlist[3],
                                                    parnumlist[4], parnumlist[5])] = False
                                else:
                                    if self.FilterPar(parnum, conditions0, conditions1, conditions2,
                                                      parnumlist[0],
                                                      parnumlist[1], parnumlist[2], parnumlist[3],
                                                      parnumlist[4], parnumlist[5]):
                                        self.pardict['%d,%d,%d,%d,%d,%d' % (
                                            parnumlist[0], parnumlist[1], parnumlist[2],
                                            parnumlist[3],
                                            parnumlist[4], parnumlist[5])] = False
                        else:
                            if self.FilterPar(parnum, conditions0, conditions1, conditions2,
                                              parnumlist[0],
                                              parnumlist[1], parnumlist[2], parnumlist[3],
                                              parnumlist[4], parnumlist[5]):
                                self.pardict['%d,%d,%d,%d,%d,%d' % (
                                    parnumlist[0], parnumlist[1], parnumlist[2],
                                    parnumlist[3],
                                    parnumlist[4], parnumlist[5])] = False
                else:
                    if self.FilterPar(parnum, conditions0, conditions1, conditions2,
                                      parnumlist[0],
                                      parnumlist[1], parnumlist[2], parnumlist[3],
                                      parnumlist[4], parnumlist[5]):
                        self.pardict['%d,%d,%d,%d,%d,%d' % (
                            parnumlist[0], parnumlist[1], parnumlist[2],
                            parnumlist[3],
                            parnumlist[4], parnumlist[5])] = False
        else:
            if self.FilterPar(parnum, conditions0, conditions1, conditions2,
                              parnumlist[0],
                              parnumlist[1], parnumlist[2], parnumlist[3],
                              parnumlist[4], parnumlist[5]):
                self.pardict['%d,%d,%d,%d,%d,%d' % (
                    parnumlist[0], parnumlist[1], parnumlist[2],
                    parnumlist[3],
                    parnumlist[4], parnumlist[5])] = False

        processnum = min(globalvar.DialogBackTestPoint.comBox_process.currentIndex() + 1, globalvar.totaltasknum)
        print('processnum: ' + str(processnum))
        print('refreshfrequency' + str(refreshfrequency))
        globalvar.DialogBackTestPoint.Log('回测选项：' + globalvar.DialogBackTestPoint.comBox_process.currentText())
        globalvar.DialogBackTestPoint.Log('待回测参数组：【' + str(globalvar.totaltasknum) + '】个')
        globalvar.DialogBackTestPoint.Log('实际开启【' + str(processnum) + '】个进程')
        globalvar.DialogBackTestPoint.table_thispargroup.clear()
        globalvar.DialogBackTestPoint.table_thispargroup.setRowCount(0)
        globalvar.DialogBackTestPoint.table_thispargroup.setHorizontalHeaderLabels(
            ['', '操作', '参数1', '参数2', '参数3', '参数4', '参数5', '参数6', '权益', '收益率', '胜率', '盈亏比', '交易次数', '夏普率', '索提诺比率2'])
        self.Initprocess(processnum, refreshfrequency, adjustment, slippoint, period, 0)

    # 继续执行下一个合约的量化回测
    def OnStartNext(self, parnum, refreshfrequency, adjustment, conditions0, conditions1, conditions2, fileid):
        globalvar.DialogBackTestPoint.backteststate = 1
        globalvar.DialogBackTestPoint.table_lefttop.setEditTriggers(QTableView.NoEditTriggers)
        globalvar.DialogBackTestPoint.btn_ok.setStyleSheet("QPushButton{border-image: url(onstopbacktest1.png)}")
        globalvar.DialogBackTestPoint.btn_ok.setFixedSize(256, 70)
        globalvar.DialogBackTestPoint.list_backtestlog.clear()
        globalvar.DialogBackTestPoint.taskprogressBar.setProperty("value", 0)
        globalvar.DialogBackTestPoint.taskprogressBar.setFormat("参数组回测任务进度 0%")
        globalvar.DialogBackTestPoint.Log("开始测试【" + globalvar.DialogBackTestPoint.strategyname + '】')
        for i in range(globalvar.DialogBackTestPoint.table_leftbottom.rowCount()):
            if globalvar.DialogBackTestPoint.table_leftbottom.item(i, 1).checkState():
                '''try:
                    globalvar.DialogBackTestPoint.Log(
                        '将回测：' + globalvar.DialogBackTestPoint.table_leftbottom.item(i,
                                                                                     1).text() + ' ,时间段：' + globalvar.DialogBackTestPoint.table_leftbottom.item(
                            i, 2).text() + '~' + globalvar.DialogBackTestPoint.table_leftbottom.item(i, 3).text())
                except os.error:
                '''
                globalvar.DialogBackTestPoint.Log(
                    '将回测：' + globalvar.DialogBackTestPoint.table_leftbottom.item(i, 1).text())
        parnumlist = [0, 0, 0, 0, 0, 0]
        for rowid in range(parnum):
            begin = int(globalvar.DialogBackTestPoint.table_lefttop.item(rowid, 1).text())
            end = int(globalvar.DialogBackTestPoint.table_lefttop.item(rowid, 2).text())
            step = int(globalvar.DialogBackTestPoint.table_lefttop.item(rowid, 3).text())
            parnumlist[rowid] = (end - begin + 1) / max(1, step)

        globalvar.totaltasknum = 0
        globalvar.finishtasknum = 0
        self.pardict.clear()
        self.pardict = {}

        slippoint = globalvar.DialogBackTestPoint.comBox_slippoint.currentIndex() + 1
        period = globalvar.DialogBackTestPoint.comBox_period.currentText()
        if globalvar.DialogBackTestPoint.table_lefttop.item(0, 1):
            for parnumlist[0] in range(int(globalvar.DialogBackTestPoint.table_lefttop.item(0, 1).text()),
                                       int(globalvar.DialogBackTestPoint.table_lefttop.item(0, 2).text()) + 1,
                                       int(globalvar.DialogBackTestPoint.table_lefttop.item(0, 3).text())):
                if globalvar.DialogBackTestPoint.table_lefttop.item(1, 1):
                    for parnumlist[1] in range(int(globalvar.DialogBackTestPoint.table_lefttop.item(1, 1).text()),
                                               int(globalvar.DialogBackTestPoint.table_lefttop.item(1, 2).text()) + 1,
                                               int(globalvar.DialogBackTestPoint.table_lefttop.item(1, 3).text())):
                        if globalvar.DialogBackTestPoint.table_lefttop.item(2, 1):
                            for parnumlist[2] in range(
                                    int(globalvar.DialogBackTestPoint.table_lefttop.item(2, 1).text()),
                                    int(globalvar.DialogBackTestPoint.table_lefttop.item(2, 2).text()) + 1,
                                    int(globalvar.DialogBackTestPoint.table_lefttop.item(2, 3).text())):
                                if globalvar.DialogBackTestPoint.table_lefttop.item(3, 1):
                                    for parnumlist[3] in range(
                                            int(globalvar.DialogBackTestPoint.table_lefttop.item(3, 1).text()),
                                            int(globalvar.DialogBackTestPoint.table_lefttop.item(3, 2).text()) + 1,
                                            int(globalvar.DialogBackTestPoint.table_lefttop.item(3, 3).text())):
                                        if globalvar.DialogBackTestPoint.table_lefttop.item(4, 1):
                                            for parnumlist[4] in range(
                                                    int(globalvar.DialogBackTestPoint.table_lefttop.item(4, 1).text()),
                                                    int(globalvar.DialogBackTestPoint.table_lefttop.item(4,
                                                                                                         2).text()) + 1,
                                                    int(globalvar.DialogBackTestPoint.table_lefttop.item(4, 3).text())):
                                                if globalvar.DialogBackTestPoint.table_lefttop.item(5, 1):
                                                    for parnumlist[5] in range(
                                                            int(globalvar.DialogBackTestPoint.table_lefttop.item(5,
                                                                                                                 1).text()),
                                                            int(globalvar.DialogBackTestPoint.table_lefttop.item(5,
                                                                                                                 2).text()) + 1,
                                                            int(globalvar.DialogBackTestPoint.table_lefttop.item(5,
                                                                                                                 3).text())):
                                                        if self.FilterPar(parnum, conditions0, conditions1, conditions2,
                                                                          parnumlist[0],
                                                                          parnumlist[1], parnumlist[2], parnumlist[3],
                                                                          parnumlist[4], parnumlist[5]):
                                                            self.pardict['%d,%d,%d,%d,%d,%d' % (
                                                                parnumlist[0], parnumlist[1], parnumlist[2],
                                                                parnumlist[3],
                                                                parnumlist[4], parnumlist[5])] = False


                                                else:
                                                    if self.FilterPar(parnum, conditions0, conditions1, conditions2,
                                                                      parnumlist[0],
                                                                      parnumlist[1], parnumlist[2], parnumlist[3],
                                                                      parnumlist[4], parnumlist[5]):
                                                        self.pardict['%d,%d,%d,%d,%d,%d' % (
                                                            parnumlist[0], parnumlist[1], parnumlist[2],
                                                            parnumlist[3],
                                                            parnumlist[4], parnumlist[5])] = False

                                        else:
                                            if self.FilterPar(parnum, conditions0, conditions1, conditions2,
                                                              parnumlist[0],
                                                              parnumlist[1], parnumlist[2], parnumlist[3],
                                                              parnumlist[4], parnumlist[5]):
                                                self.pardict['%d,%d,%d,%d,%d,%d' % (
                                                    parnumlist[0], parnumlist[1], parnumlist[2],
                                                    parnumlist[3],
                                                    parnumlist[4], parnumlist[5])] = False

                                else:
                                    if self.FilterPar(parnum, conditions0, conditions1, conditions2,
                                                      parnumlist[0],
                                                      parnumlist[1], parnumlist[2], parnumlist[3],
                                                      parnumlist[4], parnumlist[5]):
                                        self.pardict['%d,%d,%d,%d,%d,%d' % (
                                            parnumlist[0], parnumlist[1], parnumlist[2],
                                            parnumlist[3],
                                            parnumlist[4], parnumlist[5])] = False

                        else:
                            if self.FilterPar(parnum, conditions0, conditions1, conditions2,
                                              parnumlist[0],
                                              parnumlist[1], parnumlist[2], parnumlist[3],
                                              parnumlist[4], parnumlist[5]):
                                self.pardict['%d,%d,%d,%d,%d,%d' % (
                                    parnumlist[0], parnumlist[1], parnumlist[2],
                                    parnumlist[3],
                                    parnumlist[4], parnumlist[5])] = False
                else:
                    if self.FilterPar(parnum, conditions0, conditions1, conditions2,
                                      parnumlist[0],
                                      parnumlist[1], parnumlist[2], parnumlist[3],
                                      parnumlist[4], parnumlist[5]):
                        self.pardict['%d,%d,%d,%d,%d,%d' % (
                            parnumlist[0], parnumlist[1], parnumlist[2],
                            parnumlist[3],
                            parnumlist[4], parnumlist[5])] = False
        else:
            if self.FilterPar(parnum, conditions0, conditions1, conditions2,
                              parnumlist[0],
                              parnumlist[1], parnumlist[2], parnumlist[3],
                              parnumlist[4], parnumlist[5]):
                self.pardict['%d,%d,%d,%d,%d,%d' % (
                    parnumlist[0], parnumlist[1], parnumlist[2],
                    parnumlist[3],
                    parnumlist[4], parnumlist[5])] = False
        processnum = min(globalvar.DialogBackTestPoint.comBox_process.currentIndex() + 1, globalvar.totaltasknum)
        print('processnum: ' + str(processnum))
        globalvar.DialogBackTestPoint.Log('回测选项：' + globalvar.DialogBackTestPoint.comBox_process.currentText())
        globalvar.DialogBackTestPoint.Log('待回测参数组：【' + str(globalvar.totaltasknum) + '】个')
        globalvar.DialogBackTestPoint.Log('实际开启【' + str(processnum) + '】个进程')
        globalvar.DialogBackTestPoint.Log('提示：正在升级本回测模块')
        globalvar.DialogBackTestPoint.table_thispargroup.clear()
        globalvar.DialogBackTestPoint.table_thispargroup.setRowCount(0)
        globalvar.DialogBackTestPoint.table_thispargroup.setHorizontalHeaderLabels(
            ['', '操作', '参数1', '参数2', '参数3', '参数4', '参数5', '参数6', '权益', '收益率', '胜率', '盈亏比', '交易次数', '夏普率', '索提诺比率1'])
        self.Initprocess(processnum, refreshfrequency, adjustment, slippoint, period, fileid)

    # 停止量化回测
    def OnStop(self, text):
        globalvar.DialogBackTestPoint.closestate = True
        globalvar.DialogBackTestPoint.backteststate = 0
        globalvar.DialogBackTestPoint.table_lefttop.setEditTriggers(QTableView.CurrentChanged)
        if text == '完成回测':
            globalvar.DialogBackTestPoint.btn_ok.setStyleSheet("QPushButton{border-image: url(onrestartbacktest1.png)}")
            globalvar.DialogBackTestPoint.btn_ok.setFixedSize(256, 70)
        else:
            globalvar.DialogBackTestPoint.btn_ok.setStyleSheet("QPushButton{border-image: url(onstartbacktest1.png)}")
            globalvar.DialogBackTestPoint.btn_ok.setFixedSize(256, 70)

        globalvar.DialogBackTestPoint.Log(text + "【" + globalvar.DialogBackTestPoint.strategyname + '】')

    def OnStopAndNext(self, fileid):
        # globalvar.DialogBackTestPoint.callback_backtest_processbar([0, '数据载入进度%p%'])
        globalvar.DialogBackTestPoint.closestate = True
        globalvar.DialogBackTestPoint.backteststate = 0
        globalvar.DialogBackTestPoint.OnCloseProcesswindow()
        globalvar.pool.close()
        globalvar.pool.terminate()
        globalvar.DialogBackTestPoint.OnStart_Next(fileid)
